import Ax, { AxiosHeaders } from 'axios'
import type { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import to from 'await-to-js'
import is from '../utils/is'
import { message } from 'ant-design-vue'
import { useMainStore } from '@/store/useMainStore'
import { ResponseCodeEnum } from '@/core/enums/type'
import { useUserExpired } from '@/hooks/useUserExpired'

enum CommonMsg {
  success = '提交成功',
  error = '提交失败，请稍候再试...',
  networkError = '网络请求错误，请稍候再试...'
}

export const errorMessage = (err: string, duration = 3) => {
  message.error(err, duration)
}

interface Options {
  withMessage: boolean
  ignoreValid: boolean
}

export interface IAxiosRequestConfig extends InternalAxiosRequestConfig {
  uuid?: string | number | symbol
}

type AdapterOptions<T> = {
  config: Partial<IAxiosRequestConfig>
  callback?: (params: AxiosResponse<T>) => void | Error
  error?: (error: AxiosResponse<T> | Error) => void
  done?: () => void
}

interface IAxiosInstance<T> {
  (config: Partial<IAxiosRequestConfig>): Promise<AxiosResponse<T>>
}

export function adapter<T>(service: IAxiosInstance<T>) {
  return async (options: AdapterOptions<T>) => {
    const { config, callback, error, done } = options
    const [err, res] = await to(service(config))

    if (is.func(done)) {
      done()
    }

    if (!err && callback) {
      const result = callback(res!)

      if (is.error(result) && is.func(error)) {
        error(result)
      }
    }

    if (err && is.func(error)) {
      error(err)
    }

    return [err, res!] as const
  }
}

export default class Request {
  public instance: AxiosInstance

  protected config: Partial<IAxiosRequestConfig> = {}

  protected options: Partial<Options> = {}

  constructor(config: Partial<IAxiosRequestConfig> = {}, options: Partial<Options> = {}) {
    this.config = {
      baseURL: import.meta.env.VITE_API_URL,
      timeout: 700000,
      withCredentials: true,
      ...config
    }

    this.options = options

    this.instance = Ax.create(this.config)
    this.interceptorReq()
    this.interceptorRes()
  }

  protected interceptorReq() {
    this.instance.interceptors.request.use(
      (config) => {
        const mainStore = useMainStore()
        config.headers = new AxiosHeaders({
          ...config.headers,
          token: mainStore.token,
          userId: mainStore.userId
        })

        if (this.options.ignoreValid) {
          return config
        }

        if (['GET', 'get'].includes(config.method!)) {
          config.params = {
            ...config.params,
            userId: mainStore.userId
          }

          // 只有当comboId存在时才调用cibaotaocs和存储到localStorage
          if (config.params.comboId !== undefined && config.params.comboId !== null) {
            mainStore.cibaotaocs(config.params.comboId)
            localStorage.setItem('comboIduisd', config.params.comboId)
          }
        }

        if (['POST', 'post', 'put', 'PUT'].includes(config.method!)) {
          if (is.formData(config.data)) {
            config.data.set('userId', `${mainStore.userId}`)
          } else if (is.plainObject(config.data)) {
            config.data = {
              ...config.data,
              userId: mainStore.userId
            }
          }
        }

        return config
      },
      (error) => error
    )
  }

  protected interceptorRes() {
    this.instance.interceptors.response.use(
      (response) => {
        if (this.options.ignoreValid) {
          return response
        }

        return this.codeInspector(response)
      },
      async (error) => {
        if (this.options?.withMessage) {
          errorMessage(error.message || CommonMsg.networkError)
        }

        if (Ax.isCancel(error)) {
          return Promise.reject(error)
        }

        return this.codeInspector(error.response)
      }
    )
  }

  protected codeInspector(response: AxiosResponse<any, any>) {
    const code = response?.data?.code
    const { expired } = useUserExpired()
    const msg = response?.data?.msg || response?.data?.message || '网络错误，请稍候再试'

    if (Number(code) === ResponseCodeEnum.TokenExpired) {
      if (this.options.withMessage) {
        errorMessage(msg, 2)
      }

      expired()
      return Promise.reject(new Error(msg))
    }

    if (Number(code) !== ResponseCodeEnum.Success) {
      const msg = response?.data?.msg || response?.data?.message || '网络错误，请稍候再试'
      if (this.options.withMessage) {
        errorMessage(msg, 2)
      }

      return Promise.reject(new Error(msg))
    }

    return response
  }
}

export const request = new Request().instance
export const withMessage = new Request({}, { withMessage: true }).instance
export const withIgnoreValid = new Request({}, { ignoreValid: true }).instance
